#include <string.h>
#include <svc.h>		
#include <stdio.h>
#include <ctype.h>


#include <EMVResult.hpp>
//#include "emvcall.h"
#include "d3des.h"
#include "APACSpin.h"
//#include <EMVutils.hpp>

int inAPACS_OWF(unsigned char *D_value, unsigned char *K_value, unsigned char *result)
{
	unsigned char X_value[8]="\xA5\xC7\xB2\x82\x84\x76\xA8\x29";
	unsigned char Y_value[8]="\xB5\xE3\x7F\xC5\xD4\xF7\xA3\x93";
	unsigned char D_buffer[8];
	unsigned char K_buffer[8];
	int i;

	for(i=0; i<8; i++)
		{
		D_buffer[i]=D_value[i]^X_value[i];
		K_buffer[i]=K_value[i]^Y_value[i];
		}

	for(i=0; i<8; i++)
		K_buffer[i]=makeOdd(K_buffer[i]);

	deskey(K_buffer,1);
	des(result, D_buffer);

	for(i=0; i<8; i++)
		result[i]=result[i]^D_buffer[i];

	return(ROK);
}

unsigned char makeOdd(unsigned char byte)
{
	unsigned char pos = 2;
	unsigned char res;
	int bitsSet = 0;

    while (1)
    {
        if (byte & pos)
            bitsSet++;
        if (pos == 128)
            break;
        pos <<= 1;
    }

	res = byte;
	if ((bitsSet % 2) == 0) 
		res |= 1;
	else
        res = (res & 1) ^ res;
	
	return (res);
}

int inAPACSclearPINblock(char *track2, char *pin, char *pinblock)
{

	int len;
	int i,j;
	unsigned char tmp[16];
	char PAN[20];

	len=strlen(track2);
	i=0;
	while(!isdigit(track2[i++])) if(i==len) return(RERROR);
	j=0;
	while(isdigit(track2[i]) && i<=len && j<20)
		PAN[j++]=track2[i++];
	PAN[j]=0;
	len=strlen(pin);
	sprintf(pinblock, "0%1.1d%s", len, pin);
	memset(&pinblock[len+2], 'F', 16-len-2); 
	pinblock[16]=0;
	ascii_to_bin(tmp, (unsigned char *)pinblock, 16);
	memset(&tmp[8], 0x00, 8);
	ascii_to_bin(&tmp[8+2], (unsigned char *)&PAN[strlen(PAN)-12-1], 12);
	for(i=0;i<8;i++) tmp[i]^=tmp[i+8];
	hex_2_asc((unsigned char*)pinblock, tmp, 8);

	return(ROK);

}

int OnlinePIN(unsigned char *buffer, int keyset, int operation)
{

	static struct
		{
		unsigned char 	seed[8];
		unsigned char		current[8];
		} key[10];

	unsigned char pin[13];
	unsigned char val_A[4], val_B[4], val_C[4], val_D[4], KEYVAL[8], CardKey[8];
	unsigned char tmp[16+1];
	unsigned char pinblock[8];
	int i,j;
	int len;
	int cfs;
	int retVal;

	retVal=ROK;
	switch(operation)
		{
		case ADD_NEW_KEY:
			memcpy(key[keyset].seed, buffer, 8);
			memcpy(key[keyset].current, buffer, 8);
			break;

		case RESET_KEY:
			memcpy(key[keyset].current, key[keyset].seed, 8);
			break;
			
		case BUILD_PINBLOCK:
			len=strlen((char *)buffer);
			i=0;
			cfs=0;
			while(!isdigit(buffer[i++]));
			while(isdigit(buffer[i])) i++;
			cfs=i;
			memset(tmp, '0', 16);
			tmp[16]=0;
			j=15;
			for(i=cfs-1; i>=0; i--)
				if(isdigit(buffer[i])) 
					tmp[j--]=buffer[i];
			ascii_to_bin(val_A, tmp, 8);
			ascii_to_bin(val_B, &tmp[8], 8);
			memset(tmp, '0', 16);
			tmp[16]=0;
			i=cfs+5;
			j=0;
			while(buffer[i]!=0) tmp[j++]=buffer[i++];
			ascii_to_bin(val_C, buffer, 8);
			ascii_to_bin(val_D, &buffer[8], 8);
			if((retVal=getUsrPin(pin))>0)
				{
				inAPACSclearPINblock((char*)buffer, (char*)pin, (char*)tmp);
				ascii_to_bin(pinblock, tmp, 16);
				deskey(key[keyset].current, 1);
				des(pinblock, pinblock);
				hex_2_asc(buffer, pinblock, 8);
				memcpy(tmp, val_A, 4);
				memcpy(&tmp[4], val_B, 4);
				inAPACS_OWF(tmp, key[keyset].current, KEYVAL);
				memcpy(tmp, val_B, 4);
				memcpy(&tmp[4], val_D, 4);
				memcpy(&tmp[8], val_A, 4);
				memcpy(&tmp[12], val_C, 4);
				inAPACS_OWF(tmp, &tmp[8], CardKey);
				inAPACS_OWF(CardKey, KEYVAL, key[keyset].current);
				}
			break;

		}
	return(retVal);
}
